package surf.browser;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;


import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JPanel;

import org.mozilla.browser.impl.MozillaCanvas;

import surf.browser.component.TabPane;


/**
 * A container which manages all the tabs open in the web browser.
 * Tabs are stored recursively (meaning the first tab contains a reference to the second tab,
 * the second tab contains a reference to the third, etc.).
 * 
 * @author Tom Cashman
 *
 */
public class TabManager extends JPanel implements Runnable {
	private static final long serialVersionUID = 1L;
	private WebBrowser m_browser;
	private Tab m_tab;
	private TabPane m_show;
	private JButton m_addTab;
	
	/**
	 * Default constructor. Requires the web browser itself to be passed as parameter.
	 * @param wb
	 */
	public TabManager(WebBrowser wb) {
		m_browser = wb;
		//Set up the add button.
		m_addTab = new JButton(new ImageIcon("res/Add.png"));
		m_addTab.setSize(24, 24);
		m_addTab.setVisible(true);
		m_addTab.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				openNewTab();
			}
		});
		m_tab = new Tab(new TabPane(this));
		
		//Set up the layout of the panel
		this.setSize(800, 24);
		this.setMaximumSize(new Dimension(9000, 24));
		this.setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
		this.setLocation(0, 0);
		this.setVisible(true);
		
		//Add a new tab
		this.add(m_tab);
		this.add(m_addTab);
		m_browser.getContentPane().add(m_tab.getTab());
	}
	
	/**
	 * Returns the root tab
	 * @return
	 */
	public Tab getRootTab() {
		return m_tab;
	}
	
	/**
	 * Opens a new tab
	 */
	public void openNewTab() {
		if(m_tab != null)
			show(m_tab.openNewTab(m_browser));
		else {
			m_tab = new Tab(new TabPane(this));
			m_browser.getContentPane().add(m_tab.getTab());
		}
		reload();
	}
	
	/**
	 * Opens a new tab with a specified url
	 */
	public void openNewTab(String url) {
		if(m_tab != null)
			show(m_tab.openNewTab(m_browser, url));
		else {
			m_tab = new Tab(new TabPane(this, url));
			m_browser.getContentPane().add(m_tab.getTab());
		}
		reload();
	}
	
	/**
	 * Setups up all the tab buttons in the panel.
	 */
	public void reload() {
		this.removeAll();
		this.repaint();
		
		Tab t = m_tab;
		while(t != null) {
			this.add(t);
			t = t.getTail();
		}
		this.add(m_addTab);
	}
	
	/**
	 * Closes a tab.
	 * @param pane
	 */
	public void close(TabPane pane) {
		//Stop pane loading.
		pane.getBrowser().stop();
		/* 
		 * Check if the tab requested to be closed is the root tab.
		 * If so, remove it and set the root tab as its tail. 
		 */
		if(pane.equals(m_tab.getTab())) {
			m_browser.remove(m_tab.getTab());
			m_browser.repaint();
			if(m_tab.getTail() == null) {
				m_tab = null;
			} else {
				m_tab = m_tab.getTail();
			}
		} else {
			/*
			 * Else, link the tab before the closing tab to the tail of the closing tab.
			 */
			Tab t = m_tab;
			while(t != null) {
				if(t.getTail().getTab().equals(pane)) {
					Tab tail = t.getTail().getTail();
					t.setTail(tail);
					break;
				}
				t = t.getTail();
			}
		}
		try {
			/*
			 * Try to safely detach the mozilla canvas. This is needed to avoid JNI crashes (which kill the JVM).
			 */
			MozillaCanvas mc = pane.getBrowser().getMozillaContainer().getMozillaCanvas();
			mc.removeNotify();
			mc.destroyHandle();
			pane.getBrowser().removeAll();
			pane.setVisible(false);
			pane.removeAll();
		} catch (Exception e) {}
		/*
		 * Set the pane to null so the garbage collector finds it quicker and then reload the tab panel.
		 */
		pane = null;
		this.reload();
	}
	
	/**
	 * Sets a tab as the current one.
	 * @param pane
	 */
	public void show(TabPane pane) {
		m_show = pane;
		new Thread(this).start();
	}

	/**
	 * Quickly hides all tabs (run as a thread).
	 */
	public void run() {
		this.reload();
		Tab t = m_tab;
		while(t != null) {
			if(!t.getTab().equals(m_show)) {
				t.getTab().setVisible(false);
				t.getTab().setButtonText();
			}
			t = t.getTail();
		}
	}
}